﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P3SS.Networking;
using System.Xml.Serialization;
using P3SS.Security;

namespace P3SS.Engine
{
	/// <summary>
	/// Collects mail from a POP3 account
	/// </summary>
	public class Pop3Engine : AbstractEngine
	{
		#region Constructors
		/// <summary>
		/// Default constructor
		/// </summary>
		public Pop3Engine()
		{
			protocol = new Pop3Protocol();
			CollectedUniqueIDs = new List<string>();
			LeaveMailOnServer = false;
			cancellationPending = false;
		}

		/// <summary>
		/// Copy Constructor
		/// </summary>
		/// <param name="inPop3Engine">Pop3Engine object to deep copy</param>
		public Pop3Engine(Pop3Engine inPop3Engine)
		{
			protocol = new Pop3Protocol(inPop3Engine.Protocol);
			CollectedUniqueIDs = new List<string>(inPop3Engine.CollectedUniqueIDs);
			LeaveMailOnServer = inPop3Engine.LeaveMailOnServer;
			cancellationPending = false;
		}
		#endregion
		#region Properties
		/// <summary>
		/// The underlying protocol
		/// </summary>
		[XmlIgnore]
		public Pop3Protocol Protocol
		{
			get
			{ return (Pop3Protocol)protocol; }
			set
			{ protocol = value; }
		}

		/// <summary>
		/// Use APOP authentication
		/// </summary>
		public bool UseAPOP
		{
			get
			{ return Protocol.UseAPOP; }
			set
			{ Protocol.UseAPOP = value; }
		}

		/// <summary>
		/// A list of the Unique IDs for mail items left on the server
		/// </summary>
		[XmlIgnore]
		public List<string> CollectedUniqueIDs { get; set; }

		/// <summary>
		/// Leave messages on server after collecting?
		/// </summary>
		public bool LeaveMailOnServer { get; set; }

		/// <summary>
		/// Send the CAPA command to the server?
		/// </summary>
		public bool CheckServerCapabilities
		{
			get
			{ return Protocol.CheckServerCapabilities; }
			set
			{ Protocol.CheckServerCapabilities = value; }
		}
		#endregion
		#region Public Methods
		/// <summary>
		/// Gets all mail in a POP3 account
		/// </summary>
		/// <returns>A list of all mail items collected</returns>
		public List<string> GetAllMail()
		{
			cancellationPending = false;
			List<string> output = new List<string>();
			PassMessage("Starting collection", null, MessageType.Information);
			int errors = 0;

			try
			{
				Protocol.Connect();
				if (CheckServerCapabilities)
				{
					PassDebugMessage("Server reported the following capabilities:\r\n\r\n" + Protocol.ServerCapabilities);
				}

				if (!cancellationPending)
				{
					if (LeaveMailOnServer)
					{
						Dictionary<int, string> currentUids = Protocol.GetUids();
						var uidsToCollect = from t in currentUids
											where (!CollectedUniqueIDs.Contains(t.Value))
											select t;

						foreach (KeyValuePair<int, string> thisUid in uidsToCollect)
						{
							try
							{
								string mailItem = Protocol.GetMailItem(thisUid.Key);
								output.Add(mailItem);
								CollectedUniqueIDs.Add(thisUid.Value);
							}
							catch (ProtocolErrorException ex)
							{
								PassMessage(ex.Message, ex.ToString(), MessageType.Error);
								errors++;
							}

							if (cancellationPending)
							{
								break;
							}
						}
					}
					else
					{
						int numMessagesOnServer = Protocol.GetNumberMailWaiting();

						for (int i = 1; i <= numMessagesOnServer; i++)
						{
							try
							{
								string mailItem = Protocol.GetMailItem(i);
								output.Add(mailItem);
								Protocol.DeleteMailItem(i);
							}
							catch (ProtocolErrorException ex)
							{
								PassMessage(ex.Message, ex.ToString(), MessageType.Error);
								errors++;
							}

							if (cancellationPending)
							{
								break;
							}
						}
					}
				}

				Protocol.Disconnect();
				PassMessage(string.Format("Collection successful. Collected {0} mail items. There were {1} errors during collection.", output.Count, errors), null, MessageType.Information);
			}
			catch (Exception ex)
			{
				PassMessage(ex.Message, ex.ToString(), MessageType.Error);
				PassMessage(string.Format("Collection was interrupted due to an unrecoverable error.\r\n Collected {0} mail items. There were {1} other errors during collection.", output.Count, errors), null, MessageType.Information);
			}

			return output;
		}

		/// <summary>
		/// Human readable string representing this object
		/// </summary>
		/// <returns>Human readable string specifying the username, server and port</returns>
		public override string ToString()
		{
			return Username + "@@" + base.ToString();
		}

		/// <summary>
		/// Compares this Pop3Engine to another Pop3Engine. Note that CollectedUniqueIDs are not compared
		/// </summary>
		/// <param name="obj">Pop3Engine to compare</param>
		/// <returns>True if settings are the same, otherwise false</returns>
		public override bool Equals(object obj)
		{
			bool isEquals = false;

			if (obj is Pop3Engine)
			{
				Pop3Engine otherPop3Engine = (Pop3Engine)obj;

				if ((LeaveMailOnServer == otherPop3Engine.LeaveMailOnServer) && (Protocol.Equals(otherPop3Engine.Protocol)))
				{
					isEquals = true;
				}
			}

			return isEquals;
		}

		/// <summary>
		/// Creates a unique ID for this object
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		#endregion
	}
}
